#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/head.h>

extern unsigned long _text, _etext, _end;

unsigned long high_memory = 0;

int nr_free_pages = 0;
unsigned long free_page_list = 0;

unsigned short *mem_map = NULL; // 管理物理地址

unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) {
    unsigned long *pg_dir;
    unsigned long *pg_table;
    unsigned long tmp;
    unsigned long address;

    start_mem = PAGE_ALIGN(start_mem);
    address = PAGE_OFFSET;
    pg_dir = swapper_pg_dir;
    pg_dir[0] = 0;               // 取消低地址的映射
    pg_dir += PAGE_OFFSET >> 22; // 定位到内核的页表
    // 遍历页目录项
    while (address < end_mem) {
        tmp = *pg_dir;
        if (!tmp) {
            tmp = __pa(start_mem) | PAGE_TABLE;
            start_mem += PAGE_SIZE;
        }
        *pg_dir = tmp;
        pg_dir++;
        pg_table = (unsigned long *)__va(tmp & PAGE_MASK);
        // 遍历页表项
        for (tmp = 0; tmp < PTRS_PER_PAGE; tmp++, pg_table++) {
            if (address < end_mem)
                *pg_table = __pa(address) | PAGE_SHARED;
            else
                *pg_table = 0;
            address += PAGE_SIZE;
        }
    }
    invalidate(); // 刷新页目录
    return start_mem;
}

void mem_init(unsigned long start_mem, unsigned long end_mem) {
    unsigned long start_low_mem;
    int codepages = 0;
    int reservedpages = 0;
    int datapages = 0;
    unsigned long tmp;
    unsigned short *p;

    end_mem &= PAGE_MASK;
    high_memory = end_mem;
    start_mem += 0x0000000f;
    start_mem &= ~0x0000000f;
    tmp = MAP_NR(end_mem);

    mem_map = (unsigned short *)start_mem;
    p = mem_map + tmp;
    start_mem = (unsigned long)p;
    memset(mem_map, 0, start_mem - (unsigned long)mem_map);
    while (p > mem_map)
        *(--p) = MAP_PAGE_RESERVED;
    start_low_mem = PAGE_OFFSET;
    start_mem = PAGE_ALIGN(start_mem);
    // 将0到0xA0000的物理页面标记为可以使用
    while (start_low_mem < __va(0xA0000)) {
        mem_map[MAP_NR(start_low_mem)] = 0;
        start_low_mem += PAGE_SIZE;
    }
    // 将start_mem到end_mem的物理页面标记为可以使用
    while (start_mem < end_mem) {
        mem_map[MAP_NR(start_mem)] = 0;
        start_mem += PAGE_SIZE;
    }

    free_page_list = 0;
    nr_free_pages = 0;
    for (tmp = PAGE_OFFSET; tmp < end_mem; tmp += PAGE_SIZE) {
        if (mem_map[MAP_NR(tmp)]) {
            if (tmp >= (unsigned long)&_text && tmp < (unsigned long)&_end) {
                if (tmp < (unsigned long)&_etext)
                    codepages++;
                else
                    datapages++;
            } else
                reservedpages++;
            continue;
        }
        *(unsigned long *)tmp = free_page_list;
        free_page_list = tmp;
        nr_free_pages++;
    }
    tmp = nr_free_pages << PAGE_SHIFT;
    printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data)\n",
           tmp >> 10,
           __pa(end_mem) >> 10,
           codepages << (PAGE_SHIFT - 10),
           reservedpages << (PAGE_SHIFT - 10),
           datapages << (PAGE_SHIFT - 10));
}